/*
 * Copyright 2005-2013 finalist.cn. All rights reserved.
 * Support: http://www.finalist.cn
 * License: http://www.finalist.cn/license
 */
package com.finalist.dao.impl;

import java.math.BigDecimal;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.Date;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;
import java.util.regex.Pattern;

import javax.annotation.Resource;
import javax.persistence.FlushModeType;
import javax.persistence.TypedQuery;
import javax.persistence.criteria.CriteriaBuilder;
import javax.persistence.criteria.CriteriaQuery;
import javax.persistence.criteria.Join;
import javax.persistence.criteria.Path;
import javax.persistence.criteria.Predicate;
import javax.persistence.criteria.Root;
import javax.persistence.criteria.Subquery;

import com.finalist.Filter;
import com.finalist.Order;
import com.finalist.Page;
import com.finalist.Pageable;
import com.finalist.Setting;
import com.finalist.dao.GoodsDao;
import com.finalist.dao.ProductDao;
import com.finalist.dao.SnDao;
import com.finalist.entity.Attribute;
import com.finalist.entity.Brand;
import com.finalist.entity.Goods;
import com.finalist.entity.Member;
import com.finalist.entity.Order.OrderStatus;
import com.finalist.entity.Order.PaymentStatus;
import com.finalist.entity.OrderItem;
import com.finalist.entity.Product;
import com.finalist.entity.Product.OrderType;
import com.finalist.entity.Product.RecommendType;
import com.finalist.entity.ProductCategory;
import com.finalist.entity.Promotion;
import com.finalist.entity.Sn.Type;
import com.finalist.entity.SpecificationValue;
import com.finalist.entity.Tag;
import com.finalist.util.DateUtil;
import com.finalist.util.SettingUtils;

import org.apache.commons.lang.StringUtils;
import org.apache.commons.lang.builder.CompareToBuilder;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.stereotype.Repository;
import org.springframework.util.Assert;

/**
 * Dao - 商品
 * 
 * @author Finalist IT Group
 * @version 3.0
 */
@Repository("productDaoImpl")
public class ProductDaoImpl extends BaseDaoImpl<Product, Long> implements
		ProductDao {
    private static final Log log = LogFactory.getLog(ProductDaoImpl.class);
	private static final Pattern pattern = Pattern.compile("\\d*");

	@Resource(name = "goodsDaoImpl")
	private GoodsDao goodsDao;
	@Resource(name = "snDaoImpl")
	private SnDao snDao;

	public boolean snExists(String sn) {
		if (sn == null) {
			return false;
		}
		String jpql = "select count(*) from Product product where lower(product.sn) = lower(:sn)";
		Long count = entityManager.createQuery(jpql, Long.class)
				.setFlushMode(FlushModeType.COMMIT).setParameter("sn", sn)
				.getSingleResult();
		return count > 0;
	}

	public Product findBySn(String sn) {
		if (sn == null) {
			return null;
		}
		String jpql = "select product from Product product where lower(product.sn) = lower(:sn)";
		try {
			return entityManager.createQuery(jpql, Product.class)
					.setFlushMode(FlushModeType.COMMIT).setParameter("sn", sn)
					.getSingleResult();
		} catch (Exception e) {
        	e.printStackTrace();
        	log.error(e.toString());
			return null;
		}
	}

	public Product findByProductIdErp(String productIdErp) {
		if (productIdErp == null) {
			return null;
		}
		String jpql = "select product from Product product where product.productIdErp = :productIdErp";
		try {
			return entityManager.createQuery(jpql, Product.class)
					.setFlushMode(FlushModeType.COMMIT)
					.setParameter("productIdErp", productIdErp)
					.getSingleResult();
		} catch (Exception e) {
        	e.printStackTrace();
        	log.error(e.toString());
			return null;
		}
	}

	public List<Product> search(String keyword, Boolean isGift, Integer count) {
		if (StringUtils.isEmpty(keyword)) {
			return Collections.<Product> emptyList();
		}
		CriteriaBuilder criteriaBuilder = entityManager.getCriteriaBuilder();
		CriteriaQuery<Product> criteriaQuery = criteriaBuilder
				.createQuery(Product.class);
		Root<Product> root = criteriaQuery.from(Product.class);
		criteriaQuery.select(root);
		Predicate restrictions = criteriaBuilder.conjunction();
		if (pattern.matcher(keyword).matches()) {
			restrictions = criteriaBuilder.and(restrictions, criteriaBuilder
					.or(criteriaBuilder.equal(root.get("id"),
							Long.valueOf(keyword)), criteriaBuilder.like(
							root.<String> get("sn"), "%" + keyword + "%"),
							criteriaBuilder.like(root.<String> get("fullName"),
									"%" + keyword + "%")));
		} else {
			restrictions = criteriaBuilder.and(restrictions,
					criteriaBuilder.or(criteriaBuilder.like(
							root.<String> get("sn"), "%" + keyword + "%"),
							criteriaBuilder.like(root.<String> get("fullName"),
									"%" + keyword + "%")));
		}
		if (isGift != null) {
			restrictions = criteriaBuilder.and(restrictions,
					criteriaBuilder.equal(root.get("isGift"), isGift));
		}
		criteriaQuery.where(restrictions);
		criteriaQuery.orderBy(criteriaBuilder.desc(root.get("isTop")));
		return super.findList(criteriaQuery, null, count, null, null);
	}

	public List<Product> findList(ProductCategory productCategory, Brand brand,
			Promotion promotion, List<Tag> tags,
			Map<Attribute, String> attributeValue, BigDecimal startPrice,
			BigDecimal endPrice, Boolean isMarketable, Boolean isList,
			Boolean isTop, Boolean isGift, Boolean isOutOfStock,
			Boolean isStockAlert, OrderType orderType, Integer count,
			List<Filter> filters, List<Order> orders) {
		CriteriaBuilder criteriaBuilder = entityManager.getCriteriaBuilder();
		CriteriaQuery<Product> criteriaQuery = criteriaBuilder
				.createQuery(Product.class);
		Root<Product> root = criteriaQuery.from(Product.class);
		criteriaQuery.select(root);
		Predicate restrictions = criteriaBuilder.conjunction();
		if (productCategory != null) {
			restrictions = criteriaBuilder
					.and(restrictions,
							criteriaBuilder.or(
									criteriaBuilder.equal(
											root.get("productCategory"),
											productCategory),
									criteriaBuilder
											.like(root.get("productCategory")
													.<String> get("treePath"),
													"%"
															+ ProductCategory.TREE_PATH_SEPARATOR
															+ productCategory
																	.getId()
															+ ProductCategory.TREE_PATH_SEPARATOR
															+ "%")));
		}
		if (brand != null) {
			restrictions = criteriaBuilder.and(restrictions,
					criteriaBuilder.equal(root.get("brand"), brand));
		}
		if (promotion != null) {
			Subquery<Product> subquery1 = criteriaQuery.subquery(Product.class);
			Root<Product> subqueryRoot1 = subquery1.from(Product.class);
			subquery1.select(subqueryRoot1);
			subquery1.where(criteriaBuilder.equal(subqueryRoot1, root),
					criteriaBuilder.equal(subqueryRoot1.join("promotions"),
							promotion));

			Subquery<Product> subquery2 = criteriaQuery.subquery(Product.class);
			Root<Product> subqueryRoot2 = subquery2.from(Product.class);
			subquery2.select(subqueryRoot2);
			subquery2.where(criteriaBuilder.equal(subqueryRoot2, root),
					criteriaBuilder.equal(subqueryRoot2.join("productCategory")
							.join("promotions"), promotion));

			Subquery<Product> subquery3 = criteriaQuery.subquery(Product.class);
			Root<Product> subqueryRoot3 = subquery3.from(Product.class);
			subquery3.select(subqueryRoot3);
			subquery3.where(criteriaBuilder.equal(subqueryRoot3, root),
					criteriaBuilder.equal(
							subqueryRoot3.join("brand").join("promotions"),
							promotion));

			restrictions = criteriaBuilder.and(restrictions, criteriaBuilder
					.or(criteriaBuilder.exists(subquery1),
							criteriaBuilder.exists(subquery2),
							criteriaBuilder.exists(subquery3)));
		}
		if (tags != null && !tags.isEmpty()) {
			Subquery<Product> subquery = criteriaQuery.subquery(Product.class);
			Root<Product> subqueryRoot = subquery.from(Product.class);
			subquery.select(subqueryRoot);
			subquery.where(criteriaBuilder.equal(subqueryRoot, root),
					subqueryRoot.join("tags").in(tags));
			restrictions = criteriaBuilder.and(restrictions,
					criteriaBuilder.exists(subquery));
		}
		if (attributeValue != null) {
			for (Entry<Attribute, String> entry : attributeValue.entrySet()) {
				String propertyName = Product.ATTRIBUTE_VALUE_PROPERTY_NAME_PREFIX
						+ entry.getKey().getPropertyIndex();
				restrictions = criteriaBuilder.and(
						restrictions,
						criteriaBuilder.equal(root.get(propertyName),
								entry.getValue()));
			}
		}
		if (startPrice != null && endPrice != null
				&& startPrice.compareTo(endPrice) > 0) {
			BigDecimal temp = startPrice;
			startPrice = endPrice;
			endPrice = temp;
		}
		if (startPrice != null && startPrice.compareTo(new BigDecimal(0)) >= 0) {
			restrictions = criteriaBuilder.and(restrictions,
					criteriaBuilder.ge(root.<Number> get("price"), startPrice));
		}
		if (endPrice != null && endPrice.compareTo(new BigDecimal(0)) >= 0) {
			restrictions = criteriaBuilder.and(restrictions,
					criteriaBuilder.le(root.<Number> get("price"), endPrice));
		}
		if (isMarketable != null) {
			restrictions = criteriaBuilder.and(restrictions, criteriaBuilder
					.equal(root.get("isMarketable"), isMarketable));
		}
		if (isList != null) {
			restrictions = criteriaBuilder.and(restrictions,
					criteriaBuilder.equal(root.get("isList"), isList));
		}
		if (isTop != null) {
			restrictions = criteriaBuilder.and(restrictions,
					criteriaBuilder.equal(root.get("isTop"), isTop));
		}
		if (isGift != null) {
			restrictions = criteriaBuilder.and(restrictions,
					criteriaBuilder.equal(root.get("isGift"), isGift));
		}
		Path<Integer> stock = root.get("stock");
		Path<Integer> allocatedStock = root.get("allocatedStock");
		if (isOutOfStock != null) {
			if (isOutOfStock) {
				restrictions = criteriaBuilder.and(restrictions,
						criteriaBuilder.isNotNull(stock), criteriaBuilder
								.lessThanOrEqualTo(stock, allocatedStock));
			} else {
				restrictions = criteriaBuilder.and(restrictions,
						criteriaBuilder.or(criteriaBuilder.isNull(stock),
								criteriaBuilder.greaterThan(stock,
										allocatedStock)));
			}
		}
		if (isStockAlert != null) {
			Setting setting = SettingUtils.get();
			if (isStockAlert) {
				restrictions = criteriaBuilder.and(restrictions,
						criteriaBuilder.isNotNull(stock), criteriaBuilder
								.lessThanOrEqualTo(stock, criteriaBuilder.sum(
										allocatedStock,
										setting.getStockAlertCount())));
			} else {
				restrictions = criteriaBuilder
						.and(restrictions, criteriaBuilder.or(criteriaBuilder
								.isNull(stock), criteriaBuilder.greaterThan(
								stock,
								criteriaBuilder.sum(allocatedStock,
										setting.getStockAlertCount()))));
			}
		}
		criteriaQuery.where(restrictions);
		if (orderType == OrderType.priceAsc) {
			orders.add(Order.asc("price"));
			orders.add(Order.desc("createDate"));
		} else if (orderType == OrderType.priceDesc) {
			orders.add(Order.desc("price"));
			orders.add(Order.desc("createDate"));
		} else if (orderType == OrderType.salesDesc) {
			orders.add(Order.desc("sales"));
			orders.add(Order.desc("createDate"));
		} else if (orderType == OrderType.scoreDesc) {
			orders.add(Order.desc("score"));
			orders.add(Order.desc("createDate"));
		} else if (orderType == OrderType.dateDesc) {
			orders.add(Order.desc("createDate"));
		} else {
			orders.add(Order.desc("isTop"));
			orders.add(Order.desc("modifyDate"));
		}
		return super.findList(criteriaQuery, null, count, filters, orders);
	}

	public List<Product> findList(ProductCategory productCategory,
			Date beginDate, Date endDate, Integer first, Integer count) {
		CriteriaBuilder criteriaBuilder = entityManager.getCriteriaBuilder();
		CriteriaQuery<Product> criteriaQuery = criteriaBuilder
				.createQuery(Product.class);
		Root<Product> root = criteriaQuery.from(Product.class);
		criteriaQuery.select(root);
		Predicate restrictions = criteriaBuilder.conjunction();
		restrictions = criteriaBuilder.and(restrictions,
				criteriaBuilder.equal(root.get("isMarketable"), true));
		if (productCategory != null) {
			restrictions = criteriaBuilder
					.and(restrictions,
							criteriaBuilder.or(
									criteriaBuilder.equal(
											root.get("productCategory"),
											productCategory),
									criteriaBuilder
											.like(root.get("productCategory")
													.<String> get("treePath"),
													"%"
															+ ProductCategory.TREE_PATH_SEPARATOR
															+ productCategory
																	.getId()
															+ ProductCategory.TREE_PATH_SEPARATOR
															+ "%")));
		}
		if (beginDate != null) {
			restrictions = criteriaBuilder.and(
					restrictions,
					criteriaBuilder.greaterThanOrEqualTo(
							root.<Date> get("createDate"), beginDate));
		}
		if (endDate != null) {
			restrictions = criteriaBuilder.and(
					restrictions,
					criteriaBuilder.lessThanOrEqualTo(
							root.<Date> get("createDate"), endDate));
		}
		criteriaQuery.where(restrictions);
		criteriaQuery.orderBy(criteriaBuilder.desc(root.get("isTop")));
		return super.findList(criteriaQuery, first, count, null, null);
	}

	public List<Product> findList(Goods goods, Set<Product> excludes) {
		CriteriaBuilder criteriaBuilder = entityManager.getCriteriaBuilder();
		CriteriaQuery<Product> criteriaQuery = criteriaBuilder
				.createQuery(Product.class);
		Root<Product> root = criteriaQuery.from(Product.class);
		criteriaQuery.select(root);
		Predicate restrictions = criteriaBuilder.conjunction();
		if (goods != null) {
			restrictions = criteriaBuilder.and(restrictions,
					criteriaBuilder.equal(root.get("goods"), goods));
		}
		if (excludes != null && !excludes.isEmpty()) {
			restrictions = criteriaBuilder.and(restrictions,
					criteriaBuilder.not(root.in(excludes)));
		}
		criteriaQuery.where(restrictions);
		return entityManager.createQuery(criteriaQuery)
				.setFlushMode(FlushModeType.COMMIT).getResultList();
	}

	public List<Object[]> findSalesList(Date beginDate, Date endDate,
			Integer count) {
		CriteriaBuilder criteriaBuilder = entityManager.getCriteriaBuilder();
		CriteriaQuery<Object[]> criteriaQuery = criteriaBuilder
				.createQuery(Object[].class);
		Root<Product> product = criteriaQuery.from(Product.class);
		Join<Product, OrderItem> orderItems = product.join("orderItems");
		Join<Product, com.finalist.entity.Order> order = orderItems.join("order");
		criteriaQuery.multiselect(
				product.get("id"),
				product.get("sn"),
				product.get("name"),
				product.get("fullName"),
				product.get("price"),
				criteriaBuilder.sum(orderItems.<Integer> get("quantity")),
				criteriaBuilder.sum(criteriaBuilder.prod(
						orderItems.<Integer> get("quantity"),
						orderItems.<BigDecimal> get("price"))));
		Predicate restrictions = criteriaBuilder.conjunction();
		if (beginDate != null) {
			restrictions = criteriaBuilder.and(
					restrictions,
					criteriaBuilder.greaterThanOrEqualTo(
							order.<Date> get("createDate"), beginDate));
		}
		if (endDate != null) {
			restrictions = criteriaBuilder.and(
					restrictions,
					criteriaBuilder.lessThanOrEqualTo(
							order.<Date> get("createDate"), endDate));
		}
		restrictions = criteriaBuilder.and(restrictions, criteriaBuilder.equal(
				order.get("orderStatus"), OrderStatus.completed),
				criteriaBuilder.equal(order.get("paymentStatus"),
						PaymentStatus.paid));
		criteriaQuery.where(restrictions);
		criteriaQuery.groupBy(product.get("id"), product.get("sn"),
				product.get("name"), product.get("fullName"),
				product.get("price"));
		criteriaQuery.orderBy(criteriaBuilder.desc(criteriaBuilder
				.sum(criteriaBuilder.prod(orderItems.<Integer> get("quantity"),
						orderItems.<BigDecimal> get("price")))));
		TypedQuery<Object[]> query = entityManager.createQuery(criteriaQuery)
				.setFlushMode(FlushModeType.COMMIT);
		if (count != null && count >= 0) {
			query.setMaxResults(count);
		}
		return query.getResultList();
	}
	
	public Page<Product> findPageByWeiXin(ProductCategory productCategory, Brand brand,
			Promotion promotion, List<Tag> tags,
			Map<Attribute, String> attributeValue, BigDecimal startPrice,
			BigDecimal endPrice, Boolean isMarketable, Boolean isList,
			Boolean isTop, Boolean isGift, Boolean isOutOfStock,
			Boolean isStockAlert, OrderType orderType, Pageable pageable) {
		CriteriaBuilder criteriaBuilder = entityManager.getCriteriaBuilder();
		CriteriaQuery<Product> criteriaQuery = criteriaBuilder
				.createQuery(Product.class);
		Root<Product> root = criteriaQuery.from(Product.class);
		criteriaQuery.select(root);
		Predicate restrictions = criteriaBuilder.conjunction();
		if (productCategory != null) {
			restrictions = criteriaBuilder
					.and(restrictions,
							criteriaBuilder.or(
									criteriaBuilder.equal(
											root.get("productCategory"),
											productCategory),
									criteriaBuilder
											.like(root.get("productCategory")
													.<String> get("treePath"),
													"%"
															+ ProductCategory.TREE_PATH_SEPARATOR
															+ productCategory
																	.getId()
															+ ProductCategory.TREE_PATH_SEPARATOR
															+ "%")));
		}
		if (brand != null) {
			restrictions = criteriaBuilder.and(restrictions,
					criteriaBuilder.equal(root.get("brand"), brand));
		}
		if (promotion != null) {
			Subquery<Product> subquery1 = criteriaQuery.subquery(Product.class);
			Root<Product> subqueryRoot1 = subquery1.from(Product.class);
			subquery1.select(subqueryRoot1);
			subquery1.where(criteriaBuilder.equal(subqueryRoot1, root),
					criteriaBuilder.equal(subqueryRoot1.join("promotions"),
							promotion));

			Subquery<Product> subquery2 = criteriaQuery.subquery(Product.class);
			Root<Product> subqueryRoot2 = subquery2.from(Product.class);
			subquery2.select(subqueryRoot2);
			subquery2.where(criteriaBuilder.equal(subqueryRoot2, root),
					criteriaBuilder.equal(subqueryRoot2.join("productCategory")
							.join("promotions"), promotion));

			Subquery<Product> subquery3 = criteriaQuery.subquery(Product.class);
			Root<Product> subqueryRoot3 = subquery3.from(Product.class);
			subquery3.select(subqueryRoot3);
			subquery3.where(criteriaBuilder.equal(subqueryRoot3, root),
					criteriaBuilder.equal(
							subqueryRoot3.join("brand").join("promotions"),
							promotion));

			restrictions = criteriaBuilder.and(restrictions, criteriaBuilder
					.or(criteriaBuilder.exists(subquery1),
							criteriaBuilder.exists(subquery2),
							criteriaBuilder.exists(subquery3)));
		}
		if (tags != null && !tags.isEmpty()) {
			Subquery<Product> subquery = criteriaQuery.subquery(Product.class);
			Root<Product> subqueryRoot = subquery.from(Product.class);
			subquery.select(subqueryRoot);
			subquery.where(criteriaBuilder.equal(subqueryRoot, root),
					subqueryRoot.join("tags").in(tags));
			restrictions = criteriaBuilder.and(restrictions,
					criteriaBuilder.exists(subquery));
		}
		if (attributeValue != null) {
			for (Entry<Attribute, String> entry : attributeValue.entrySet()) {
				String propertyName = Product.ATTRIBUTE_VALUE_PROPERTY_NAME_PREFIX
						+ entry.getKey().getPropertyIndex();
				restrictions = criteriaBuilder.and(
						restrictions,
						criteriaBuilder.equal(root.get(propertyName),
								entry.getValue()));
			}
		}
		if (startPrice != null && endPrice != null
				&& startPrice.compareTo(endPrice) > 0) {
			BigDecimal temp = startPrice;
			startPrice = endPrice;
			endPrice = temp;
		}
		if (startPrice != null && startPrice.compareTo(new BigDecimal(0)) >= 0) {
			restrictions = criteriaBuilder.and(restrictions,
					criteriaBuilder.ge(root.<Number> get("weiPrice"), startPrice));
		}
		if (endPrice != null && endPrice.compareTo(new BigDecimal(0)) >= 0) {
			restrictions = criteriaBuilder.and(restrictions,
					criteriaBuilder.le(root.<Number> get("weiPrice"), endPrice));
		}
		if (isMarketable != null) {
			restrictions = criteriaBuilder.and(restrictions, criteriaBuilder
					.equal(root.get("isMarketable"), isMarketable));
		}
		//微信上传的商品
		restrictions = criteriaBuilder.and(restrictions, criteriaBuilder
				.equal(root.get("isWei"), true));
		if (isList != null) {
			restrictions = criteriaBuilder.and(restrictions,
					criteriaBuilder.equal(root.get("isList"), isList));
		}

		if (isTop != null) {
			restrictions = criteriaBuilder.and(restrictions,
					criteriaBuilder.equal(root.get("isTop"), isTop));
		}
		if (isGift != null) {
			restrictions = criteriaBuilder.and(restrictions,
					criteriaBuilder.equal(root.get("isGift"), isGift));
		}
		Path<Integer> stock = root.get("stock");
		Path<Integer> allocatedStock = root.get("allocatedStock");
		if (isOutOfStock != null) {
			if (isOutOfStock) {
				restrictions = criteriaBuilder.and(restrictions,
						criteriaBuilder.isNotNull(stock), criteriaBuilder
								.lessThanOrEqualTo(stock, allocatedStock));
			} else {
				restrictions = criteriaBuilder.and(restrictions,
						criteriaBuilder.or(criteriaBuilder.isNull(stock),
								criteriaBuilder.greaterThan(stock,
										allocatedStock)));
			}
		}
		if (isStockAlert != null) {
			Setting setting = SettingUtils.get();
			if (isStockAlert) {
				restrictions = criteriaBuilder.and(restrictions,
						criteriaBuilder.isNotNull(stock), criteriaBuilder
								.lessThanOrEqualTo(stock, criteriaBuilder.sum(
										allocatedStock,
										setting.getStockAlertCount())));
			} else {
				restrictions = criteriaBuilder
						.and(restrictions, criteriaBuilder.or(criteriaBuilder
								.isNull(stock), criteriaBuilder.greaterThan(
								stock,
								criteriaBuilder.sum(allocatedStock,
										setting.getStockAlertCount()))));
			}
		}
		criteriaQuery.where(restrictions);
		List<Order> orders = pageable.getOrders();
		if (orderType == OrderType.priceAsc) {
			orders.add(Order.asc("weiPrice"));
			orders.add(Order.desc("createDate"));
		} else if (orderType == OrderType.priceDesc) {
			orders.add(Order.desc("weiPrice"));
			orders.add(Order.desc("createDate"));
		} else if (orderType == OrderType.salesDesc) {
			orders.add(Order.desc("sales"));
			orders.add(Order.desc("createDate"));
		} else if (orderType == OrderType.scoreDesc) {
			orders.add(Order.desc("score"));
			orders.add(Order.desc("createDate"));
		} else if (orderType == OrderType.dateDesc) {
			orders.add(Order.desc("createDate"));
		} else {
			orders.add(Order.desc("isTop"));
			orders.add(Order.desc("modifyDate"));
		}
		return super.findPage(criteriaQuery, pageable);
	}

	
	public Page<Product> findPage(ProductCategory productCategory, Brand brand,
			Promotion promotion, List<Tag> tags,
			Map<Attribute, String> attributeValue, BigDecimal startPrice,
			BigDecimal endPrice, Boolean isMarketable, Boolean isList,
			Boolean isTop, Boolean isGift, Boolean isOutOfStock,
			Boolean isStockAlert, OrderType orderType, Pageable pageable) {
		CriteriaBuilder criteriaBuilder = entityManager.getCriteriaBuilder();
		CriteriaQuery<Product> criteriaQuery = criteriaBuilder
				.createQuery(Product.class);
		Root<Product> root = criteriaQuery.from(Product.class);
		criteriaQuery.select(root);
		Predicate restrictions = criteriaBuilder.conjunction();
		if (productCategory != null) {
			restrictions = criteriaBuilder
					.and(restrictions,
							criteriaBuilder.or(
									criteriaBuilder.equal(
											root.get("productCategory"),
											productCategory),
									criteriaBuilder
											.like(root.get("productCategory")
													.<String> get("treePath"),
													"%"
															+ ProductCategory.TREE_PATH_SEPARATOR
															+ productCategory
																	.getId()
															+ ProductCategory.TREE_PATH_SEPARATOR
															+ "%")));
		}
		if (brand != null) {
			restrictions = criteriaBuilder.and(restrictions,
					criteriaBuilder.equal(root.get("brand"), brand));
		}
		if (promotion != null) {
			Subquery<Product> subquery1 = criteriaQuery.subquery(Product.class);
			Root<Product> subqueryRoot1 = subquery1.from(Product.class);
			subquery1.select(subqueryRoot1);
			subquery1.where(criteriaBuilder.equal(subqueryRoot1, root),
					criteriaBuilder.equal(subqueryRoot1.join("promotions"),
							promotion));

			Subquery<Product> subquery2 = criteriaQuery.subquery(Product.class);
			Root<Product> subqueryRoot2 = subquery2.from(Product.class);
			subquery2.select(subqueryRoot2);
			subquery2.where(criteriaBuilder.equal(subqueryRoot2, root),
					criteriaBuilder.equal(subqueryRoot2.join("productCategory")
							.join("promotions"), promotion));

			Subquery<Product> subquery3 = criteriaQuery.subquery(Product.class);
			Root<Product> subqueryRoot3 = subquery3.from(Product.class);
			subquery3.select(subqueryRoot3);
			subquery3.where(criteriaBuilder.equal(subqueryRoot3, root),
					criteriaBuilder.equal(
							subqueryRoot3.join("brand").join("promotions"),
							promotion));

			restrictions = criteriaBuilder.and(restrictions, criteriaBuilder
					.or(criteriaBuilder.exists(subquery1),
							criteriaBuilder.exists(subquery2),
							criteriaBuilder.exists(subquery3)));
		}
		if (tags != null && !tags.isEmpty()) {
			Subquery<Product> subquery = criteriaQuery.subquery(Product.class);
			Root<Product> subqueryRoot = subquery.from(Product.class);
			subquery.select(subqueryRoot);
			subquery.where(criteriaBuilder.equal(subqueryRoot, root),
					subqueryRoot.join("tags").in(tags));
			restrictions = criteriaBuilder.and(restrictions,
					criteriaBuilder.exists(subquery));
		}
		if (attributeValue != null) {
			for (Entry<Attribute, String> entry : attributeValue.entrySet()) {
				String propertyName = Product.ATTRIBUTE_VALUE_PROPERTY_NAME_PREFIX
						+ entry.getKey().getPropertyIndex();
				restrictions = criteriaBuilder.and(
						restrictions,
						criteriaBuilder.equal(root.get(propertyName),
								entry.getValue()));
			}
		}
		if (startPrice != null && endPrice != null
				&& startPrice.compareTo(endPrice) > 0) {
			BigDecimal temp = startPrice;
			startPrice = endPrice;
			endPrice = temp;
		}
		if (startPrice != null && startPrice.compareTo(new BigDecimal(0)) >= 0) {
			restrictions = criteriaBuilder.and(restrictions,
					criteriaBuilder.ge(root.<Number> get("price"), startPrice));
		}
		if (endPrice != null && endPrice.compareTo(new BigDecimal(0)) >= 0) {
			restrictions = criteriaBuilder.and(restrictions,
					criteriaBuilder.le(root.<Number> get("price"), endPrice));
		}
		if (isMarketable != null) {
			restrictions = criteriaBuilder.and(restrictions, criteriaBuilder
					.equal(root.get("isMarketable"), isMarketable));
		}
		if (isList != null) {
			restrictions = criteriaBuilder.and(restrictions,
					criteriaBuilder.equal(root.get("isList"), isList));
		}

		if (isTop != null) {
			restrictions = criteriaBuilder.and(restrictions,
					criteriaBuilder.equal(root.get("isTop"), isTop));
		}
		if (isGift != null) {
			restrictions = criteriaBuilder.and(restrictions,
					criteriaBuilder.equal(root.get("isGift"), isGift));
		}
		
		
		Path<Integer> stock = root.get("stock");
		Path<Integer> allocatedStock = root.get("allocatedStock");
		if (isOutOfStock != null) {
			if (isOutOfStock) {
				restrictions = criteriaBuilder.and(restrictions,
						criteriaBuilder.isNotNull(stock), criteriaBuilder
								.lessThanOrEqualTo(stock, allocatedStock));
			} else {
				restrictions = criteriaBuilder.and(restrictions,
						criteriaBuilder.or(criteriaBuilder.isNull(stock),
								criteriaBuilder.greaterThan(stock,
										allocatedStock)));
			}
		}
		if (isStockAlert != null) {
			Setting setting = SettingUtils.get();
			if (isStockAlert) {
				restrictions = criteriaBuilder.and(restrictions,
						criteriaBuilder.isNotNull(stock), criteriaBuilder
								.lessThanOrEqualTo(stock, criteriaBuilder.sum(
										allocatedStock,
										setting.getStockAlertCount())));
			} else {
				restrictions = criteriaBuilder
						.and(restrictions, criteriaBuilder.or(criteriaBuilder
								.isNull(stock), criteriaBuilder.greaterThan(
								stock,
								criteriaBuilder.sum(allocatedStock,
										setting.getStockAlertCount()))));
			}
		}
		criteriaQuery.where(restrictions);
		List<Order> orders = pageable.getOrders();
		if (orderType == OrderType.priceAsc) {
			orders.add(Order.asc("price"));
			orders.add(Order.desc("createDate"));
		} else if (orderType == OrderType.priceDesc) {
			orders.add(Order.desc("price"));
			orders.add(Order.desc("createDate"));
		} else if (orderType == OrderType.salesDesc) {
			orders.add(Order.desc("sales"));
			orders.add(Order.desc("createDate"));
		} else if (orderType == OrderType.scoreDesc) {
			orders.add(Order.desc("score"));
			orders.add(Order.desc("createDate"));
		} else if (orderType == OrderType.dateDesc) {
			orders.add(Order.desc("createDate"));
		} else {
			orders.add(Order.desc("isTop"));
			orders.add(Order.desc("modifyDate"));
		}
		return super.findPage(criteriaQuery, pageable);
	}

	public Page<Product> findPage(ProductCategory productCategory, Brand brand,
			Promotion promotion, List<Tag> tags,
			Map<Attribute, String> attributeValue, BigDecimal startPrice,
			BigDecimal endPrice, Boolean isMarketable, Boolean isList,
			Boolean isTop, Boolean isGift, Boolean isOutOfStock,
			Boolean isStockAlert, OrderType orderType, RecommendType type,
			Pageable pageable) {
		CriteriaBuilder criteriaBuilder = entityManager.getCriteriaBuilder();
		CriteriaQuery<Product> criteriaQuery = criteriaBuilder
				.createQuery(Product.class);
		Root<Product> root = criteriaQuery.from(Product.class);
		criteriaQuery.select(root);
		Predicate restrictions = criteriaBuilder.conjunction();
		if (productCategory != null) {
			restrictions = criteriaBuilder
					.and(restrictions,
							criteriaBuilder.or(
									criteriaBuilder.equal(
											root.get("productCategory"),
											productCategory),
									criteriaBuilder
											.like(root.get("productCategory")
													.<String> get("treePath"),
													"%"
															+ ProductCategory.TREE_PATH_SEPARATOR
															+ productCategory
																	.getId()
															+ ProductCategory.TREE_PATH_SEPARATOR
															+ "%")));
		}
		if (brand != null) {
			restrictions = criteriaBuilder.and(restrictions,
					criteriaBuilder.equal(root.get("brand"), brand));
		}
		if (promotion != null) {
			Subquery<Product> subquery1 = criteriaQuery.subquery(Product.class);
			Root<Product> subqueryRoot1 = subquery1.from(Product.class);
			subquery1.select(subqueryRoot1);
			subquery1.where(criteriaBuilder.equal(subqueryRoot1, root),
					criteriaBuilder.equal(subqueryRoot1.join("promotions"),
							promotion));

			Subquery<Product> subquery2 = criteriaQuery.subquery(Product.class);
			Root<Product> subqueryRoot2 = subquery2.from(Product.class);
			subquery2.select(subqueryRoot2);
			subquery2.where(criteriaBuilder.equal(subqueryRoot2, root),
					criteriaBuilder.equal(subqueryRoot2.join("productCategory")
							.join("promotions"), promotion));

			Subquery<Product> subquery3 = criteriaQuery.subquery(Product.class);
			Root<Product> subqueryRoot3 = subquery3.from(Product.class);
			subquery3.select(subqueryRoot3);
			subquery3.where(criteriaBuilder.equal(subqueryRoot3, root),
					criteriaBuilder.equal(
							subqueryRoot3.join("brand").join("promotions"),
							promotion));

			restrictions = criteriaBuilder.and(restrictions, criteriaBuilder
					.or(criteriaBuilder.exists(subquery1),
							criteriaBuilder.exists(subquery2),
							criteriaBuilder.exists(subquery3)));
		}
		if (tags != null && !tags.isEmpty()) {
			Subquery<Product> subquery = criteriaQuery.subquery(Product.class);
			Root<Product> subqueryRoot = subquery.from(Product.class);
			subquery.select(subqueryRoot);
			subquery.where(criteriaBuilder.equal(subqueryRoot, root),
					subqueryRoot.join("tags").in(tags));
			restrictions = criteriaBuilder.and(restrictions,
					criteriaBuilder.exists(subquery));
		}
		if (attributeValue != null) {
			for (Entry<Attribute, String> entry : attributeValue.entrySet()) {
				String propertyName = Product.ATTRIBUTE_VALUE_PROPERTY_NAME_PREFIX
						+ entry.getKey().getPropertyIndex();
				restrictions = criteriaBuilder.and(
						restrictions,
						criteriaBuilder.equal(root.get(propertyName),
								entry.getValue()));
			}
		}
		if (startPrice != null && endPrice != null
				&& startPrice.compareTo(endPrice) > 0) {
			BigDecimal temp = startPrice;
			startPrice = endPrice;
			endPrice = temp;
		}
		if (startPrice != null && startPrice.compareTo(new BigDecimal(0)) >= 0) {
			restrictions = criteriaBuilder.and(restrictions,
					criteriaBuilder.ge(root.<Number> get("price"), startPrice));
		}
		if (endPrice != null && endPrice.compareTo(new BigDecimal(0)) >= 0) {
			restrictions = criteriaBuilder.and(restrictions,
					criteriaBuilder.le(root.<Number> get("price"), endPrice));
		}
		if (isMarketable != null) {
			restrictions = criteriaBuilder.and(restrictions, criteriaBuilder
					.equal(root.get("isMarketable"), isMarketable));
		}
		if (isList != null) {
			restrictions = criteriaBuilder.and(restrictions,
					criteriaBuilder.equal(root.get("isList"), isList));
		}
		if (type != null) {
			restrictions = criteriaBuilder.and(restrictions,
					criteriaBuilder.equal(root.get("recommendType"), type));
		}
		if (isTop != null) {
			restrictions = criteriaBuilder.and(restrictions,
					criteriaBuilder.equal(root.get("isTop"), isTop));
		}
		if (isGift != null) {
			restrictions = criteriaBuilder.and(restrictions,
					criteriaBuilder.equal(root.get("isGift"), isGift));
		}
		Path<Integer> stock = root.get("stock");
		Path<Integer> allocatedStock = root.get("allocatedStock");
		if (isOutOfStock != null) {
			if (isOutOfStock) {
				restrictions = criteriaBuilder.and(restrictions,
						criteriaBuilder.isNotNull(stock), criteriaBuilder
								.lessThanOrEqualTo(stock, allocatedStock));
			} else {
				restrictions = criteriaBuilder.and(restrictions,
						criteriaBuilder.or(criteriaBuilder.isNull(stock),
								criteriaBuilder.greaterThan(stock,
										allocatedStock)));
			}
		}
		if (isStockAlert != null) {
			Setting setting = SettingUtils.get();
			if (isStockAlert) {
				restrictions = criteriaBuilder.and(restrictions,
						criteriaBuilder.isNotNull(stock), criteriaBuilder
								.lessThanOrEqualTo(stock, criteriaBuilder.sum(
										allocatedStock,
										setting.getStockAlertCount())));
			} else {
				restrictions = criteriaBuilder
						.and(restrictions, criteriaBuilder.or(criteriaBuilder
								.isNull(stock), criteriaBuilder.greaterThan(
								stock,
								criteriaBuilder.sum(allocatedStock,
										setting.getStockAlertCount()))));
			}
		}
		criteriaQuery.where(restrictions);
		List<Order> orders = pageable.getOrders();
		if (orderType == OrderType.priceAsc) {
			orders.add(Order.asc("price"));
			orders.add(Order.desc("createDate"));
		} else if (orderType == OrderType.priceDesc) {
			orders.add(Order.desc("price"));
			orders.add(Order.desc("createDate"));
		} else if (orderType == OrderType.salesDesc) {
			orders.add(Order.desc("sales"));
			orders.add(Order.desc("createDate"));
		} else if (orderType == OrderType.scoreDesc) {
			orders.add(Order.desc("score"));
			orders.add(Order.desc("createDate"));
		} else if (orderType == OrderType.dateDesc) {
			orders.add(Order.desc("createDate"));
		} else {
			orders.add(Order.desc("isTop"));
			orders.add(Order.desc("modifyDate"));
		}
		return super.findPage(criteriaQuery, pageable);
	}

	public Page<Product> findPage(Member member, Pageable pageable) {
		if (member == null) {
			return new Page<Product>(Collections.<Product> emptyList(), 0,
					pageable);
		}
		CriteriaBuilder criteriaBuilder = entityManager.getCriteriaBuilder();
		CriteriaQuery<Product> criteriaQuery = criteriaBuilder
				.createQuery(Product.class);
		Root<Product> root = criteriaQuery.from(Product.class);
		criteriaQuery.select(root);
		criteriaQuery.where(criteriaBuilder.equal(root.join("favoriteMembers"),
				member));
		return super.findPage(criteriaQuery, pageable);
	}

	public Long count(Member favoriteMember, Boolean isMarketable,
			Boolean isList, Boolean isTop, Boolean isGift,
			Boolean isOutOfStock, Boolean isStockAlert) {
		CriteriaBuilder criteriaBuilder = entityManager.getCriteriaBuilder();
		CriteriaQuery<Product> criteriaQuery = criteriaBuilder
				.createQuery(Product.class);
		Root<Product> root = criteriaQuery.from(Product.class);
		criteriaQuery.select(root);
		Predicate restrictions = criteriaBuilder.conjunction();
		if (favoriteMember != null) {
			restrictions = criteriaBuilder.and(restrictions, criteriaBuilder
					.equal(root.join("favoriteMembers"), favoriteMember));
		}
		if (isMarketable != null) {
			restrictions = criteriaBuilder.and(restrictions, criteriaBuilder
					.equal(root.get("isMarketable"), isMarketable));
		}
		if (isList != null) {
			restrictions = criteriaBuilder.and(restrictions,
					criteriaBuilder.equal(root.get("isList"), isList));
		}
		if (isTop != null) {
			restrictions = criteriaBuilder.and(restrictions,
					criteriaBuilder.equal(root.get("isTop"), isTop));
		}
		if (isGift != null) {
			restrictions = criteriaBuilder.and(restrictions,
					criteriaBuilder.equal(root.get("isGift"), isGift));
		}
		Path<Integer> stock = root.get("stock");
		Path<Integer> allocatedStock = root.get("allocatedStock");
		if (isOutOfStock != null) {
			if (isOutOfStock) {
				restrictions = criteriaBuilder.and(restrictions,
						criteriaBuilder.isNotNull(stock), criteriaBuilder
								.lessThanOrEqualTo(stock, allocatedStock));
			} else {
				restrictions = criteriaBuilder.and(restrictions,
						criteriaBuilder.or(criteriaBuilder.isNull(stock),
								criteriaBuilder.greaterThan(stock,
										allocatedStock)));
			}
		}
		if (isStockAlert != null) {
			Setting setting = SettingUtils.get();
			if (isStockAlert) {
				restrictions = criteriaBuilder.and(restrictions,
						criteriaBuilder.isNotNull(stock), criteriaBuilder
								.lessThanOrEqualTo(stock, criteriaBuilder.sum(
										allocatedStock,
										setting.getStockAlertCount())));
			} else {
				restrictions = criteriaBuilder
						.and(restrictions, criteriaBuilder.or(criteriaBuilder
								.isNull(stock), criteriaBuilder.greaterThan(
								stock,
								criteriaBuilder.sum(allocatedStock,
										setting.getStockAlertCount()))));
			}
		}
		criteriaQuery.where(restrictions);
		return super.count(criteriaQuery, null);
	}

	public boolean isPurchased(Member member, Product product) {
		if (member == null || product == null) {
			return false;
		}
		String jqpl = "select count(*) from OrderItem orderItem where orderItem.product = :product and orderItem.order.member = :member and orderItem.order.orderStatus = :orderStatus";
		Long count = entityManager.createQuery(jqpl, Long.class)
				.setFlushMode(FlushModeType.COMMIT)
				.setParameter("product", product)
				.setParameter("member", member)
				.setParameter("orderStatus", OrderStatus.completed)
				.getSingleResult();
		return count > 0;
	}

	/**
	 * 设置值并保存
	 * 
	 * @param product
	 *            商品
	 */
	@Override
	public void persist(Product product) {
		Assert.notNull(product);

		setValue(product);
		super.persist(product);
	}

	/**
	 * 设置值并更新
	 * 
	 * @param product
	 *            商品
	 * @return 商品
	 */
	@Override
	public Product merge(Product product) {
		Assert.notNull(product);

		if (!product.getIsGift()) {
			String jpql = "delete from GiftItem giftItem where giftItem.gift = :product";
			entityManager.createQuery(jpql).setFlushMode(FlushModeType.COMMIT)
					.setParameter("product", product).executeUpdate();
		}
		if (!product.getIsMarketable() || product.getIsGift()) {
			String jpql = "delete from CartItem cartItem where cartItem.product = :product";
			entityManager.createQuery(jpql).setFlushMode(FlushModeType.COMMIT)
					.setParameter("product", product).executeUpdate();
		}
		setValue(product);
		return super.merge(product);
	}

	@Override
	public void remove(Product product) {
		if (product != null) {
			Goods goods = product.getGoods();
			if (goods != null && goods.getProducts() != null) {
				goods.getProducts().remove(product);
				if (goods.getProducts().isEmpty()) {
					goodsDao.remove(goods);
				}
			}
		}
		super.remove(product);
	}

	/**
	 * 设置值
	 * 
	 * @param product
	 *            商品
	 */
	private void setValue(Product product) {
		if (product == null) {
			return;
		}
		if (StringUtils.isEmpty(product.getSn())) {
			String sn;
			do {
				sn = snDao.generate(Type.product);
			} while (snExists(sn));
			product.setSn(sn);
		}
		StringBuffer fullName = new StringBuffer(product.getName());
		if (product.getSpecificationValues() != null
				&& !product.getSpecificationValues().isEmpty()) {
			List<SpecificationValue> specificationValues = new ArrayList<SpecificationValue>(
					product.getSpecificationValues());
			Collections.sort(specificationValues,
					new Comparator<SpecificationValue>() {
						public int compare(SpecificationValue a1,
								SpecificationValue a2) {
							return new CompareToBuilder().append(
									a1.getSpecification(),
									a2.getSpecification()).toComparison();
						}
					});
			fullName.append(Product.FULL_NAME_SPECIFICATION_PREFIX);
			int i = 0;
			for (Iterator<SpecificationValue> iterator = specificationValues
					.iterator(); iterator.hasNext(); i++) {
				if (i != 0) {
					fullName.append(Product.FULL_NAME_SPECIFICATION_SEPARATOR);
				}
				fullName.append(iterator.next().getName());
			}
			fullName.append(Product.FULL_NAME_SPECIFICATION_SUFFIX);
		}
		product.setFullName(fullName.toString());
	}

	/**
	 * 查询关联商品 wangxm
	 * 
	 * @param id
	 *            商品id
	 * @param has_agreement
	 *            是否是合约机
	 * @param recommendType
	 *            关联类型
	 * @param count
	 *            数量
	 * @return
	 */
	@Override
	public List<Product> recommendlist(Long id, Boolean has_agreement,
			RecommendType recommendType, Integer count) {
		TypedQuery<Product> query = null;
		String jpql = null;
		if (id != null) {
			if (has_agreement == null
					&& recommendType == RecommendType.yun_third) {
				jpql = "select product from Product product where product.isMiao = true and product.isMarketable = true and product.recommendType != :type2 and  product.combinationType is null and product.id != :id order by product.sales desc";
				query = (TypedQuery<Product>) entityManager
						.createQuery(jpql, Product.class)
						.setFlushMode(FlushModeType.COMMIT)
						.setParameter("id", id)
						.setParameter("type2", RecommendType.yun_third);
			}

			if (has_agreement == null
					&& recommendType != RecommendType.yun_third) {
				jpql = "select product from Product product where product.id != :id and product.isMiao = true and product.isMarketable = true and product.combinationType is null order by product.sales desc";
				query = (TypedQuery<Product>) entityManager
						.createQuery(jpql, Product.class)
						.setFlushMode(FlushModeType.COMMIT)
						.setParameter("id", id);
			}

			if (has_agreement != null && has_agreement == true) {
				if (recommendType == RecommendType.duan_one) {
					jpql = "select product from Product product where product.recommendType = :type1 and product.isMiao = true and product.isMarketable = true and product.has_agreement = false and product.combinationType is null and product.id != :id order by product.sales desc";
					query = (TypedQuery<Product>) entityManager
							.createQuery(jpql, Product.class)
							.setFlushMode(FlushModeType.COMMIT)
							.setParameter("type1", RecommendType.duan_one)
//							.setParameter("type2", RecommendType.yun_third)
							.setParameter("id", id);
				} else if (recommendType == RecommendType.duan_two) {
					jpql = "select product from Product product where product.recommendType = :type0 and product.has_agreement = false and product.isMiao = true and product.isMarketable = true and product.combinationType is null and product.id != :id order by product.sales desc";
					query = (TypedQuery<Product>) entityManager
							.createQuery(jpql, Product.class)
							.setFlushMode(FlushModeType.COMMIT)
							.setParameter("type0", RecommendType.duan_two)
//							.setParameter("type2", RecommendType.yun_third)
							.setParameter("id", id);
				}
			}
			if (has_agreement != null && has_agreement == false) {
				if (recommendType == RecommendType.duan_one) {
					jpql = "select product from Product product where product.recommendType = :type1 and product.isMiao = true and product.isMarketable = true and product.combinationType is null and product.id != :id order by product.sales desc";
					query = (TypedQuery<Product>) entityManager
							.createQuery(jpql, Product.class)
							.setFlushMode(FlushModeType.COMMIT)
							.setParameter("type1", RecommendType.duan_one)
//							.setParameter("type2", RecommendType.yun_third)
							.setParameter("id", id);
				} else if (recommendType == RecommendType.duan_two) {
					jpql = "select product from Product product where product.recommendType = :type0 and product.isMiao = true and product.isMarketable = true and product.combinationType is null and product.id != :id order by product.sales desc";
					query = (TypedQuery<Product>) entityManager
							.createQuery(jpql, Product.class)
							.setFlushMode(FlushModeType.COMMIT)
							.setParameter("type0", RecommendType.duan_two)
//							.setParameter("type2", RecommendType.yun_third)
							.setParameter("id", id);
				}
			}
			if (count != null && query != null) {
				query.setMaxResults(count);
				return query.getResultList();
			} else {
				return null;
			}
		} else {
			return null;
		}
	}

	@Override
	public Page<Product> findPage(ProductCategory productCategory, Brand brand,
			Promotion promotion, List<Tag> tags,
			Map<Attribute, String> attributeValue, BigDecimal startPrice,
			BigDecimal endPrice, Boolean isMarketable, Boolean isList,
			Boolean isTop, Boolean isGift, Boolean isOutOfStock,
			Boolean isStockAlert, OrderType orderType, RecommendType type,
			Boolean isMiao, Boolean isWei, Boolean isApp, Pageable pageable) {
		CriteriaBuilder criteriaBuilder = entityManager.getCriteriaBuilder();
		CriteriaQuery<Product> criteriaQuery = criteriaBuilder
				.createQuery(Product.class);
		Root<Product> root = criteriaQuery.from(Product.class);
		criteriaQuery.select(root);
		Predicate restrictions = criteriaBuilder.conjunction();
		if (productCategory != null) {
			restrictions = criteriaBuilder
					.and(restrictions,
							criteriaBuilder.or(
									criteriaBuilder.equal(
											root.get("productCategory"),
											productCategory),
									criteriaBuilder
											.like(root.get("productCategory")
													.<String> get("treePath"),
													"%"
															+ ProductCategory.TREE_PATH_SEPARATOR
															+ productCategory
																	.getId()
															+ ProductCategory.TREE_PATH_SEPARATOR
															+ "%")));
		}
		if (brand != null) {
			restrictions = criteriaBuilder.and(restrictions,
					criteriaBuilder.equal(root.get("brand"), brand));
		}
		if (promotion != null) {
			Subquery<Product> subquery1 = criteriaQuery.subquery(Product.class);
			Root<Product> subqueryRoot1 = subquery1.from(Product.class);
			subquery1.select(subqueryRoot1);
			subquery1.where(criteriaBuilder.equal(subqueryRoot1, root),
					criteriaBuilder.equal(subqueryRoot1.join("promotions"),
							promotion));

			Subquery<Product> subquery2 = criteriaQuery.subquery(Product.class);
			Root<Product> subqueryRoot2 = subquery2.from(Product.class);
			subquery2.select(subqueryRoot2);
			subquery2.where(criteriaBuilder.equal(subqueryRoot2, root),
					criteriaBuilder.equal(subqueryRoot2.join("productCategory")
							.join("promotions"), promotion));

			Subquery<Product> subquery3 = criteriaQuery.subquery(Product.class);
			Root<Product> subqueryRoot3 = subquery3.from(Product.class);
			subquery3.select(subqueryRoot3);
			subquery3.where(criteriaBuilder.equal(subqueryRoot3, root),
					criteriaBuilder.equal(
							subqueryRoot3.join("brand").join("promotions"),
							promotion));

			restrictions = criteriaBuilder.and(restrictions, criteriaBuilder
					.or(criteriaBuilder.exists(subquery1),
							criteriaBuilder.exists(subquery2),
							criteriaBuilder.exists(subquery3)));
		}
		if (tags != null && !tags.isEmpty()) {
			Subquery<Product> subquery = criteriaQuery.subquery(Product.class);
			Root<Product> subqueryRoot = subquery.from(Product.class);
			subquery.select(subqueryRoot);
			subquery.where(criteriaBuilder.equal(subqueryRoot, root),
					subqueryRoot.join("tags").in(tags));
			restrictions = criteriaBuilder.and(restrictions,
					criteriaBuilder.exists(subquery));
		}
		if (attributeValue != null) {
			for (Entry<Attribute, String> entry : attributeValue.entrySet()) {
				String propertyName = Product.ATTRIBUTE_VALUE_PROPERTY_NAME_PREFIX
						+ entry.getKey().getPropertyIndex();
				restrictions = criteriaBuilder.and(
						restrictions,
						criteriaBuilder.equal(root.get(propertyName),
								entry.getValue()));
			}
		}
		if (startPrice != null && endPrice != null
				&& startPrice.compareTo(endPrice) > 0) {
			BigDecimal temp = startPrice;
			startPrice = endPrice;
			endPrice = temp;
		}
		if (startPrice != null && startPrice.compareTo(new BigDecimal(0)) >= 0) {
			restrictions = criteriaBuilder.and(restrictions,
					criteriaBuilder.ge(root.<Number> get("price"), startPrice));
		}
		if (endPrice != null && endPrice.compareTo(new BigDecimal(0)) >= 0) {
			restrictions = criteriaBuilder.and(restrictions,
					criteriaBuilder.le(root.<Number> get("price"), endPrice));
		}
		if (isMarketable != null) {
			restrictions = criteriaBuilder.and(restrictions, criteriaBuilder
					.equal(root.get("isMarketable"), isMarketable));
		}
		if (isList != null) {
			restrictions = criteriaBuilder.and(restrictions,
					criteriaBuilder.equal(root.get("isList"), isList));
		}
		if (type != null) {
			restrictions = criteriaBuilder.and(restrictions,
					criteriaBuilder.equal(root.get("recommendType"), type));
		}
		if (isTop != null) {
			restrictions = criteriaBuilder.and(restrictions,
					criteriaBuilder.equal(root.get("isTop"), isTop));
		}
		if (isGift != null) {
			restrictions = criteriaBuilder.and(restrictions,
					criteriaBuilder.equal(root.get("isGift"), isGift));
		}
		if (isMiao != null) {
			restrictions = criteriaBuilder.and(restrictions,
					criteriaBuilder.equal(root.get("isMiao"), isMiao));
		}
		if (isWei != null) {
			restrictions = criteriaBuilder.and(restrictions,
					criteriaBuilder.equal(root.get("isWei"), isWei));
		}
		if (isApp != null) {
			restrictions = criteriaBuilder.and(restrictions,
					criteriaBuilder.equal(root.get("isApp"), isApp));
		}
		Path<Integer> stock = root.get("stock");
		Path<Integer> allocatedStock = root.get("allocatedStock");
		if (isOutOfStock != null) {
			if (isOutOfStock) {
				restrictions = criteriaBuilder.and(restrictions,
						criteriaBuilder.isNotNull(stock), criteriaBuilder
								.lessThanOrEqualTo(stock, allocatedStock));
			} else {
				restrictions = criteriaBuilder.and(restrictions,
						criteriaBuilder.or(criteriaBuilder.isNull(stock),
								criteriaBuilder.greaterThan(stock,
										allocatedStock)));
			}
		}
		if (isStockAlert != null) {
			Setting setting = SettingUtils.get();
			if (isStockAlert) {
				restrictions = criteriaBuilder.and(restrictions,
						criteriaBuilder.isNotNull(stock), criteriaBuilder
								.lessThanOrEqualTo(stock, criteriaBuilder.sum(
										allocatedStock,
										setting.getStockAlertCount())));
			} else {
				restrictions = criteriaBuilder
						.and(restrictions, criteriaBuilder.or(criteriaBuilder
								.isNull(stock), criteriaBuilder.greaterThan(
								stock,
								criteriaBuilder.sum(allocatedStock,
										setting.getStockAlertCount()))));
			}
		}
		criteriaQuery.where(restrictions);
		List<Order> orders = pageable.getOrders();
		if (orderType == OrderType.priceAsc) {
			orders.add(Order.asc("price"));
			orders.add(Order.desc("createDate"));
		} else if (orderType == OrderType.priceDesc) {
			orders.add(Order.desc("price"));
			orders.add(Order.desc("createDate"));
		} else if (orderType == OrderType.salesDesc) {
			orders.add(Order.desc("sales"));
			orders.add(Order.desc("createDate"));
		} else if (orderType == OrderType.scoreDesc) {
			orders.add(Order.desc("score"));
			orders.add(Order.desc("createDate"));
		} else if (orderType == OrderType.dateDesc) {
			orders.add(Order.desc("createDate"));
		} else {
			orders.add(Order.desc("isTop"));
			orders.add(Order.desc("modifyDate"));
		}
		return super.findPage(criteriaQuery, pageable);
	}

	@Override
	public Page<Product> findPage(ProductCategory productCategory, Brand brand,
			Promotion promotion, List<Tag> tags,
			Map<Attribute, String> attributeValue, BigDecimal startPrice,
			BigDecimal endPrice, Boolean isMarketable, Boolean isList,
			Boolean isTop, Boolean isGift, Boolean isOutOfStock,
			Boolean isStockAlert, OrderType orderType, RecommendType type,
			Boolean isMiao, Boolean isWei, Boolean isApp, Boolean isJLProduct,
			Boolean isJoin, Pageable pageable) {
		CriteriaBuilder criteriaBuilder = entityManager.getCriteriaBuilder();
		CriteriaQuery<Product> criteriaQuery = criteriaBuilder
				.createQuery(Product.class);
		Root<Product> root = criteriaQuery.from(Product.class);
		criteriaQuery.select(root);
		Predicate restrictions = criteriaBuilder.conjunction();
		if (productCategory != null) {
			restrictions = criteriaBuilder
					.and(restrictions,
							criteriaBuilder.or(
									criteriaBuilder.equal(
											root.get("productCategory"),
											productCategory),
									criteriaBuilder
											.like(root.get("productCategory")
													.<String> get("treePath"),
													"%"
															+ ProductCategory.TREE_PATH_SEPARATOR
															+ productCategory
																	.getId()
															+ ProductCategory.TREE_PATH_SEPARATOR
															+ "%")));
		}
		if (brand != null) {
			restrictions = criteriaBuilder.and(restrictions,
					criteriaBuilder.equal(root.get("brand"), brand));
		}
		if (promotion != null) {
			Subquery<Product> subquery1 = criteriaQuery.subquery(Product.class);
			Root<Product> subqueryRoot1 = subquery1.from(Product.class);
			subquery1.select(subqueryRoot1);
			subquery1.where(criteriaBuilder.equal(subqueryRoot1, root),
					criteriaBuilder.equal(subqueryRoot1.join("promotions"),
							promotion));

			Subquery<Product> subquery2 = criteriaQuery.subquery(Product.class);
			Root<Product> subqueryRoot2 = subquery2.from(Product.class);
			subquery2.select(subqueryRoot2);
			subquery2.where(criteriaBuilder.equal(subqueryRoot2, root),
					criteriaBuilder.equal(subqueryRoot2.join("productCategory")
							.join("promotions"), promotion));

			Subquery<Product> subquery3 = criteriaQuery.subquery(Product.class);
			Root<Product> subqueryRoot3 = subquery3.from(Product.class);
			subquery3.select(subqueryRoot3);
			subquery3.where(criteriaBuilder.equal(subqueryRoot3, root),
					criteriaBuilder.equal(
							subqueryRoot3.join("brand").join("promotions"),
							promotion));

			restrictions = criteriaBuilder.and(restrictions, criteriaBuilder
					.or(criteriaBuilder.exists(subquery1),
							criteriaBuilder.exists(subquery2),
							criteriaBuilder.exists(subquery3)));
		}
		if (tags != null && !tags.isEmpty()) {
			Subquery<Product> subquery = criteriaQuery.subquery(Product.class);
			Root<Product> subqueryRoot = subquery.from(Product.class);
			subquery.select(subqueryRoot);
			subquery.where(criteriaBuilder.equal(subqueryRoot, root),
					subqueryRoot.join("tags").in(tags));
			restrictions = criteriaBuilder.and(restrictions,
					criteriaBuilder.exists(subquery));
		}
		if (attributeValue != null) {
			for (Entry<Attribute, String> entry : attributeValue.entrySet()) {
				String propertyName = Product.ATTRIBUTE_VALUE_PROPERTY_NAME_PREFIX
						+ entry.getKey().getPropertyIndex();
				restrictions = criteriaBuilder.and(
						restrictions,
						criteriaBuilder.equal(root.get(propertyName),
								entry.getValue()));
			}
		}
		if (startPrice != null && endPrice != null
				&& startPrice.compareTo(endPrice) > 0) {
			BigDecimal temp = startPrice;
			startPrice = endPrice;
			endPrice = temp;
		}
		if (startPrice != null && startPrice.compareTo(new BigDecimal(0)) >= 0) {
			restrictions = criteriaBuilder.and(restrictions,
					criteriaBuilder.ge(root.<Number> get("price"), startPrice));
		}
		if (endPrice != null && endPrice.compareTo(new BigDecimal(0)) >= 0) {
			restrictions = criteriaBuilder.and(restrictions,
					criteriaBuilder.le(root.<Number> get("price"), endPrice));
		}
		if (isWei != null) {
			restrictions = criteriaBuilder.and(restrictions,
					criteriaBuilder.equal(root.get("isWei"), isWei));
		}
		if (isMiao != null) {
			restrictions = criteriaBuilder.and(restrictions,
					criteriaBuilder.equal(root.get("isMiao"), isMiao));
		}
		if (isApp != null) {
			restrictions = criteriaBuilder.and(restrictions,
					criteriaBuilder.equal(root.get("isApp"), isApp));
		}
		if (isJLProduct != null) {
			restrictions = criteriaBuilder
					.and(restrictions, criteriaBuilder.equal(
							root.get("isJLProduct"), isJLProduct));
		}
		if (isJoin != null) {
			restrictions = criteriaBuilder.and(restrictions,
					criteriaBuilder.equal(root.get("isJoin"), isJoin));
		}
		if (isMarketable != null) {
			restrictions = criteriaBuilder.and(restrictions, criteriaBuilder
					.equal(root.get("isMarketable"), isMarketable));
		}
		if (isList != null) {
			restrictions = criteriaBuilder.and(restrictions,
					criteriaBuilder.equal(root.get("isList"), isList));
		}

		if (isTop != null) {
			restrictions = criteriaBuilder.and(restrictions,
					criteriaBuilder.equal(root.get("isTop"), isTop));
		}
		if (isGift != null) {
			restrictions = criteriaBuilder.and(restrictions,
					criteriaBuilder.equal(root.get("isGift"), isGift));
		}
		Path<Integer> stock = root.get("stock");
		Path<Integer> allocatedStock = root.get("allocatedStock");
		if (isOutOfStock != null) {
			if (isOutOfStock) {
				restrictions = criteriaBuilder.and(restrictions,
						criteriaBuilder.isNotNull(stock), criteriaBuilder
								.lessThanOrEqualTo(stock, allocatedStock));
			} else {
				restrictions = criteriaBuilder.and(restrictions,
						criteriaBuilder.or(criteriaBuilder.isNull(stock),
								criteriaBuilder.greaterThan(stock,
										allocatedStock)));
			}
		}
		if (isStockAlert != null) {
			Setting setting = SettingUtils.get();
			if (isStockAlert) {
				restrictions = criteriaBuilder.and(restrictions,
						criteriaBuilder.isNotNull(stock), criteriaBuilder
								.lessThanOrEqualTo(stock, criteriaBuilder.sum(
										allocatedStock,
										setting.getStockAlertCount())));
			} else {
				restrictions = criteriaBuilder
						.and(restrictions, criteriaBuilder.or(criteriaBuilder
								.isNull(stock), criteriaBuilder.greaterThan(
								stock,
								criteriaBuilder.sum(allocatedStock,
										setting.getStockAlertCount()))));
			}
		}
		criteriaQuery.where(restrictions);
		List<Order> orders = pageable.getOrders();
		if (orderType == OrderType.priceAsc) {
			orders.add(Order.asc("price"));
			orders.add(Order.desc("createDate"));
		} else if (orderType == OrderType.priceDesc) {
			orders.add(Order.desc("price"));
			orders.add(Order.desc("createDate"));
		} else if (orderType == OrderType.salesDesc) {
			orders.add(Order.desc("sales"));
			orders.add(Order.desc("createDate"));
		} else if (orderType == OrderType.scoreDesc) {
			orders.add(Order.desc("score"));
			orders.add(Order.desc("createDate"));
		} else if (orderType == OrderType.dateDesc) {
			orders.add(Order.desc("createDate"));
		} else {
			orders.add(Order.desc("isTop"));
			orders.add(Order.desc("modifyDate"));
		}
		return super.findPage(criteriaQuery, pageable);
	}
	
	
	@Override
	public Page<Product> miaoLoveFindPage(ProductCategory productCategory,
			Boolean isWei, Boolean isMarketable, Pageable pageable) {
		CriteriaBuilder criteriaBuilder = entityManager.getCriteriaBuilder();
		CriteriaQuery<Product> criteriaQuery = criteriaBuilder
				.createQuery(Product.class);
		Root<Product> root = criteriaQuery.from(Product.class);
		criteriaQuery.select(root);
		Predicate restrictions = criteriaBuilder.conjunction();
		
		if (productCategory != null) {
			restrictions = criteriaBuilder.and(restrictions,
					criteriaBuilder.equal(root.get("productCategory"), productCategory));
		}
		if (isWei != null) {
			restrictions = criteriaBuilder.and(restrictions,
					criteriaBuilder.equal(root.get("isWei"), isWei));
		}
		if (isMarketable != null) {
			restrictions = criteriaBuilder.and(restrictions,
					criteriaBuilder.equal(root.get("isMarketable"), isMarketable));
		}
		criteriaQuery.where(restrictions);
		return super.findPage(criteriaQuery, pageable);
		
	}

	@Override
	public List<Product> findProductByPage(int pageNo) {
		try {
			String sql = "from Product where isMarketable = ? and expire  >= ?  order by isTop desc,order asc, stickTime desc";
			TypedQuery<Product> query = null;
			query = (TypedQuery<Product>)entityManager.createQuery(sql, Product.class);
			query.setParameter(1, true);
			query.setParameter(2,  DateUtil.getCurrentDate());
			query.setFirstResult((pageNo-1)*12);
			query.setMaxResults(12);
			return query.getResultList();
		} catch (Exception e) {
			e.printStackTrace();
		}
		return null;
	}

	@Override
	public List<Product> findProductByCondition(int pageNo, Boolean isPCShow, Boolean isCarousel, Long[] categorys,
			String[] origins, String[] dests, String[] starts,
			String[] timeCycle) {
		return findProductByCondition(pageNo, 10, isPCShow, isCarousel, categorys, origins, dests, starts, timeCycle);
	}
	
	@Override
	public List<Product> findProductByCondition(int pageNo, int pageSize, Boolean isPCShow, Boolean isCarousel, Long[] categorys,
			String[] origins, String[] dests, String[] starts,
			String[] timeCycle) {
		//order by createDate desc
		try {
			StringBuilder sql = new StringBuilder("from Product p where 1=1  ");
			if(categorys!=null && categorys.length > 0){
				sql.append("and p.productCategory.id in (");
				for(int i = 0 ; i < categorys.length ; i++){
					sql.append(categorys[i]+",");
				}
				sql.deleteCharAt(sql.length()-1);
				sql.append(")");
			}
			//出发地
			if(origins!=null && origins.length>0){
				sql.append(" and (");
				for(int i = 0 ; i < origins.length ; i++){
						log.info(origins[i]);
						//sql.append("p.startAddress like '%"+new String(origins[i].getBytes("ISO-8859-1"),"utf-8")+"%' or");
						sql.append("p.startAddress like '%"+origins[i]+"%' or ");
				}
				sql.append(" 1=2)");
			}
			//目的地
			if(dests!=null && dests.length>0){
				sql.append(" and (");
				for(int i = 0 ; i < dests.length ; i++){
//						sql.append("p.destination like '%"+new String(dests[i].getBytes("ISO-8859-1"),"utf-8")+"%' or");
					sql.append("p.destination like '%"+dests[i]+"%' or ");
				}
				sql.append(" 1=2)");
			}
			//何时
			if(starts!=null && starts.length>0){
				sql.append(" and (");
				for(int i = 0 ; i < starts.length ; i++){
					String year = starts[i].split("-")[0];
					String month = starts[i].split("-")[1];
					sql.append("(year(p.beginDate) = "+year+" and month(p.beginDate) = "+month+")or");
				}
				sql.append(" 1=2)");
			}
			//时间周期
			if(timeCycle!=null && timeCycle.length>0){
				sql.append(" and (");
				for(int i = 0 ; i < timeCycle.length ; i++){
					sql.append(" (day(p.beginDate)+ "+timeCycle[i]+"-1) = day(p.endDate) or  ");
				}
				sql.append(" 1=2)");
			}
			//是否PC端显示
			if (isPCShow != null) {
				if (isPCShow) {
					sql.append(" and ispcshow = 1");
				} 
			}
			//是否PC轮播显示
			if (isCarousel != null) {
				if (isCarousel) {
					sql.append(" and is_carousel = 1");
				} 
			}
			
			
			sql.append(" and isMarketable = ? and expire >= ? order by isTop desc,order asc, stickTime desc");
			TypedQuery<Product> query = null;
			query = (TypedQuery<Product>)entityManager.createQuery(sql.toString(), Product.class);
			query.setParameter(1, true);

			query.setParameter(2, DateUtil.getCurrentDate());
			query.setFirstResult((pageNo-1) * 12);
			query.setMaxResults(12);

			List<Product> list = query.getResultList();
			return list;
		} catch (Exception e) {
			e.printStackTrace();
		}
		return null;
	}

	@Override
	public List<Product> findAllTemplate() {
		String jpql = "select product from Product product where product.isTemplate = :isTemplate order by modifyDate desc";
		try {
			return entityManager.createQuery(jpql, Product.class)
					.setFlushMode(FlushModeType.COMMIT)
					.setParameter("isTemplate", true).getResultList();
		} catch (Exception e) {
        	e.printStackTrace();
        	log.error(e.toString());
			return null;
		}
	}

	@Override
	public List<Product> findStickProduct() {
		try {
			String sql = "from Product where isMarketable = ? and isTop=? order by isTop desc,order asc, stickTime desc";
			TypedQuery<Product> query = null;
			query = (TypedQuery<Product>)entityManager.createQuery(sql, Product.class);
			query.setParameter(1, true);
			query.setParameter(2, true);
			query.setMaxResults(5);
			return query.getResultList();
		} catch (Exception e) {
			e.printStackTrace();
		}
		return null;
	}

	@Override
	public List<Product> findexpireProduct() {
		String sql = "from Product where isMarketable = ? and expire  < ?  order by isTop desc,order asc, stickTime desc";
		TypedQuery<Product> query = null;
		query = (TypedQuery<Product>)entityManager.createQuery(sql, Product.class);
		query.setParameter(1, true);
		query.setParameter(2,  DateUtil.getCurrentDate());
		return query.getResultList();
	}

}